package com.dj.mingkong.admin.biz.service.music.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dj.mingkong.admin.biz.controller.music.vo.request.*;
import com.dj.mingkong.admin.biz.controller.music.vo.response.MusicResourceVo;
import com.dj.mingkong.admin.biz.controller.music.vo.response.MusicReviewVo;
import com.dj.mingkong.admin.biz.controller.music.vo.response.MusicVo;
import com.dj.mingkong.admin.biz.core.config.BaseConfig;
import com.dj.mingkong.admin.biz.repository.database.music.*;
import com.dj.mingkong.admin.biz.repository.database.system.DictionaryRepository;
import com.dj.mingkong.admin.biz.repository.elasticsearch.music.MusicEsRepository;
import com.dj.mingkong.admin.biz.service.music.MusicService;
import com.dj.mingkong.common.enums.MusicStatusEnum;
import com.dj.mingkong.common.enums.MusicianStatusEnum;
import com.dj.mingkong.common.enums.StatusEnum;
import com.dj.mingkong.common.enums.ToneTypeEnum;
import com.dj.mingkong.common.exception.ValidateException;
import com.dj.mingkong.common.infrastructure.base.dto.delete.DeleteVo;
import com.dj.mingkong.common.infrastructure.base.dto.page.PageVo;
import com.dj.mingkong.common.infrastructure.base.eo.BaseEo;
import com.dj.mingkong.common.infrastructure.eo.database.music.*;
import com.dj.mingkong.common.infrastructure.eo.elasticsearch.music.MusicEsEo;
import com.dj.mingkong.common.microservice.UserContext;
import com.dj.mingkong.common.util.AssertUtils;
import com.dj.mingkong.common.util.SmartBeanCopy;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * 音乐服务实现
 *
 * @author zenglonghui
 * @date: 2022/8/5 17:19
 */
@Slf4j
@Service("musicService")
public class MusicServiceImpl implements MusicService {
    @Resource
    private MusicRepository musicRepository;
    @Resource
    private MusicianRepository musicianRepository;
    @Resource
    private TypeRepository typeRepository;
    @Resource
    private StyleRepository styleRepository;
    @Resource
    private DictionaryRepository dictionaryRepository;
    @Resource
    private MusicReviewRepository musicReviewRepository;
    @Resource
    private MusicResourceRepository musicResourceRepository;
    @Resource
    private BaseConfig baseConfig;
    @Resource
    private AlbumMusicRepository albumMusicRepository;
    @Resource
    private AlbumRepository albumRepository;
    @Resource
    private MusicEsRepository musicEsRepository;


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void insert(MusicAddVo addVo) {
        final MusicianEo musicianEo = musicianRepository.oneByUserIdAndStatus(
                UserContext.getUserId(), MusicianStatusEnum.PASS.getIntVal());
        AssertUtils.isNotNull(musicianEo, "未入驻音乐人，请到我的音乐人申请!");
        checkMusicAddVo(addVo);
        final MusicEo musicEo = SmartBeanCopy.copy(addVo, MusicEo.class);
        musicEo.setMusicianId(musicianEo.getId());
        musicRepository.save(musicEo);
        saveMusicResource(musicEo.getId(), addVo.getMusicResourceAddVoList());
        saveAlbumMusic(musicEo.getId(), addVo.getAlbumIdList());
        saveMusicReviewVo(musicEo, addVo.getMusicReviewAddVo());
    }

    private void saveAlbumMusic(Long musicId, List<Long> albumIdList) {
        final List<AlbumMusicEo> albumMusicEos = albumMusicRepository.lambdaQuery()
                .eq(AlbumMusicEo::getMusicId, musicId).list();
        if (CollectionUtils.isNotEmpty(albumMusicEos)) {
            final List<Long> addList = albumIdList.stream().filter(aLong ->
                    !albumMusicEos.stream().map(AlbumMusicEo::getAlbumId).toList().contains(aLong)).toList();
            saveAlbumMusic(addList, musicId);
            final List<Long> delList = albumMusicEos.stream().filter(albumMusicEo ->
                    !albumIdList.contains(albumMusicEo.getAlbumId())).map(BaseEo::getId).toList();
            albumMusicRepository.deleteByIds(delList);
        } else {
            saveAlbumMusic(albumIdList, musicId);
        }
    }

    private void saveAlbumMusic(List<Long> albumIdList, Long musicId) {
        if (CollectionUtils.isNotEmpty(albumIdList)) {
            final List<AlbumMusicEo> albumMusicEos = albumIdList.stream().map(aLong ->
                    AlbumMusicEo.builder().albumId(aLong).musicId(musicId).build()).toList();
            albumMusicRepository.saveBatch(albumMusicEos);
        }
    }

    private void saveMusicResource(Long musicId, List<MusicResourceAddVo> musicResourceVoList) {
        AssertUtils.isNotNull(musicResourceVoList, "file invalid");
        final List<MusicResourceAddVo> vos = musicResourceVoList.stream()
                .filter(vo -> ToneTypeEnum.TEST.equalsType(vo.getToneType())
                        && StringUtils.isNotBlank(vo.getMusicUrl())).toList();
        AssertUtils.isNotNull(vos, "test file invalid");
        final List<MusicResourceEo> musicResourceEos = musicResourceVoList.stream()
                .map(vo -> buildResourceEo(vo, musicId))
                .filter(eo -> StringUtils.isNotBlank(eo.getMusicUrl())).toList();
        final List<MusicResourceEo> addList = musicResourceEos.stream().filter(eo -> Objects.isNull(eo.getId())).toList();
        final List<MusicResourceEo> updateList = musicResourceEos.stream().filter(eo -> Objects.nonNull(eo.getId())).toList();
        if (CollectionUtils.isNotEmpty(addList)) {
            musicResourceRepository.saveBatch(addList);
        }
        if (CollectionUtils.isNotEmpty(updateList)) {
            musicResourceRepository.updateBatchById(updateList);
        }
    }

    private MusicResourceEo buildResourceEo(MusicResourceAddVo vo, Long musicId) {
        final MusicResourceEo musicResourceEo = SmartBeanCopy.copy(vo, MusicResourceEo.class);
        if (!baseConfig.getMusicFormat().contains(musicResourceEo.getFormat())) {
            throw new ValidateException("format invalid");
        }
        musicResourceEo.setMusicId(musicId);
        return musicResourceEo;
    }

    private void saveMusicReviewVo(MusicEo musicEo, MusicReviewAddVo musicReviewVo) {
        final MusicReviewEo musicReviewEo = SmartBeanCopy.copy(musicReviewVo, MusicReviewEo.class);
        musicReviewEo.setMusicId(musicEo.getId()).setStatus(musicEo.getStatus());
        musicReviewRepository.save(musicReviewEo);
    }

    private void checkMusicAddVo(MusicAddVo addVo) {
        AssertUtils.isNotNull(typeRepository.getById(addVo.getTypeId()), "typeId invalid");
        AssertUtils.isNotNull(styleRepository.getById(addVo.getStyleId()), "styleId invalid");
        AssertUtils.isNotNull(dictionaryRepository.oneByFullKey(addVo.getLanguageKey()), "languageKey invalid");
        AssertUtils.isNotNull(dictionaryRepository.oneByFullKey(addVo.getNavMenuKey()), "navMenuKey invalid");
        AssertUtils.isNotNull(dictionaryRepository.oneByFullKey(addVo.getMusicReviewAddVo().getMsgKey()), "msgKey invalid");
        if (addVo.getAlbumIdList().size() != albumRepository.countByIds(addVo.getAlbumIdList())) {
            throw new ValidateException("albumId invalid");
        }
    }

    private void checkMusicUpdateVo(MusicUpdateVo updateVo) {
        AssertUtils.isNotNull(typeRepository.getById(updateVo.getTypeId()), "typeId invalid");
        AssertUtils.isNotNull(styleRepository.getById(updateVo.getStyleId()), "styleId invalid");
        AssertUtils.isNotNull(dictionaryRepository.oneByFullKey(updateVo.getLanguageKey()), "languageKey invalid");
        AssertUtils.isNotNull(dictionaryRepository.oneByFullKey(updateVo.getNavMenuKey()), "navMenuKey invalid");
        AssertUtils.isNotNull(dictionaryRepository.oneByFullKey(updateVo.getMusicReviewUpdateVo().getMsgKey()), "msgKey invalid");
        if (updateVo.getAlbumIdList().size() != albumRepository.countByIds(updateVo.getAlbumIdList())) {
            throw new ValidateException("albumId invalid");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateById(MusicUpdateVo updateVo) {
        checkMusicUpdateVo(updateVo);
        MusicEo musicEo = SmartBeanCopy.copy(updateVo, MusicEo.class);
        if (!MusicStatusEnum.PASS.equalsIntVal(updateVo.getStatus())) {
            musicEo = new MusicEo();
            musicEo.setStatus(updateVo.getStatus()).setId(updateVo.getId());
        }
        musicRepository.updateById(musicEo);
        if (MusicStatusEnum.PASS.equalsIntVal(updateVo.getStatus())) {
            //saveMusicResource(musicEo.getId(), updateVo.getMusicResourceUpdateVoList());
            saveAlbumMusic(musicEo.getId(), updateVo.getAlbumIdList());
            saveMusicEs(musicEo);
        }
        if (!MusicStatusEnum.PENDING.equalsIntVal(updateVo.getStatus())) {
            updateMusicReviewVo(musicEo, updateVo.getMusicReviewUpdateVo());
        }
    }

    private void saveMusicEs(MusicEo musicEo) {
        final MusicEsEo musicEsEo = SmartBeanCopy.copy(musicEo, MusicEsEo.class);
        musicEsRepository.save(musicEsEo);
    }

    private void updateMusicReviewVo(MusicEo musicEo, MusicReviewUpdateVo musicReviewVo) {
        final MusicReviewEo musicReviewEo = SmartBeanCopy.copy(musicReviewVo, MusicReviewEo.class);
        musicReviewEo.setMusicId(musicEo.getId()).setStatus(musicEo.getStatus());
        if (Objects.isNull(musicReviewEo.getId())) {
            musicReviewRepository.save(musicReviewEo);
        } else {
            musicReviewRepository.updateById(musicReviewEo);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteById(DeleteVo deleteVo) {
        musicRepository.deleteById(deleteVo.getId());
        albumMusicRepository.deleteByMusicId(deleteVo.getId());
        musicReviewRepository.deleteByMusicId(deleteVo.getId());
        musicResourceRepository.deleteByMusicId(deleteVo.getId());
    }

    @Override
    public PageVo<MusicVo> pageByVo(MusicQueryVo queryVo) {
        final Page<MusicEo> eoPage = musicRepository.listPageByVo(queryVo);
        final PageVo<MusicVo> pageVo = SmartBeanCopy.copyPageVo(eoPage, MusicVo.class);
        buildPageVo(pageVo);
        return pageVo;
    }

    private void buildPageVo(PageVo<MusicVo> pageVo) {
        for (MusicVo musicVo : pageVo.getRecords()) {
            final List<MusicReviewVo> musicReviewVoList = getMusicReviewVoList(musicVo.getId());
            musicVo.setAlbumIdList(getAlbumIdList(musicVo.getId()))
                    .setMusicReviewVoList(musicReviewVoList)
                    .setMusicReviewVo(CollectionUtils.isNotEmpty(musicReviewVoList)
                            ? musicReviewVoList.get(0) : MusicReviewVo.builder().build())
                    .setMusicResourceVoList(getMusicResourceVoList(musicVo.getId()));
        }

    }

    private List<MusicResourceVo> getMusicResourceVoList(Long musicId) {
        final List<MusicResourceEo> musicResourceEos = musicResourceRepository.lambdaQuery()
                .eq(MusicResourceEo::getMusicId, musicId)
                .eq(MusicResourceEo::getStatus, StatusEnum.VALID.getIntVal())
                .orderByAsc(MusicResourceEo::getToneType).list();
        return SmartBeanCopy.copy(musicResourceEos, MusicResourceVo.class);
    }

    private List<MusicReviewVo> getMusicReviewVoList(Long musicId) {
        final List<MusicReviewEo> musicReviewEos = musicReviewRepository.lambdaQuery()
                .eq(MusicReviewEo::getMusicId, musicId).orderByDesc(BaseEo::getCreateTime).list();
        return SmartBeanCopy.copy(musicReviewEos, MusicReviewVo.class);
    }

    private List<Long> getAlbumIdList(Long musicId) {
        final List<AlbumMusicEo> albumMusicEos = albumMusicRepository.lambdaQuery()
                .eq(AlbumMusicEo::getMusicId, musicId).list();
        return albumMusicEos.stream().map(AlbumMusicEo::getAlbumId).toList();
    }

}
